package com.fowo.api.user.service.impl;

import com.fowo.api.common.model.R;
import com.fowo.api.common.util.ListUtils;
import com.fowo.api.config.AppProperties;
import com.fowo.api.sms.service.VerificationCodeService;
import com.fowo.api.sys.entity.SysLoginLog;
import com.fowo.api.sys.entity.SysUser;
import com.fowo.api.sys.service.SysLoginLogService;
import com.fowo.api.sys.service.SysRandomImageService;
import com.fowo.api.sys.service.SysUserService;
import com.fowo.api.user.model.JwtUserInfo;
import com.fowo.api.user.model.LoginAccount;
import com.fowo.api.user.model.LoginResult;
import com.fowo.api.user.service.LoginService;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.time.Duration;

@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    private SysUserService userService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private VerificationCodeService verificationCodeService;
    @Resource
    private SysLoginLogService sysLoginLogService;
    @Resource
    private SysRandomImageService sysRandomImageService;
    @Resource
    private AppProperties appProperties;

    @Override
    public LoginResult loginByAccount(LoginAccount loginAccount) {
        // 图片验证码处理
        if (appProperties.getLoginImageCaptchaCheckMode() == AppProperties.LoginImageCaptchaCheckMode.Required) {
            if (!StringUtils.hasText(loginAccount.getCheckKey())) {
                return new LoginResult("error", "account", null, null, "NOT_CHECK_KEY", "至少必要的 checkKey 参数");
            }
        }
        if (StringUtils.hasText(loginAccount.getCheckKey()) && appProperties.getLoginImageCaptchaCheckMode() != AppProperties.LoginImageCaptchaCheckMode.Disable) {
            if (!sysRandomImageService.valid(loginAccount.getCheckKey(), loginAccount.getCaptcha())) {
                return new LoginResult("error", "account", null, null, "CAPTCHA_NOT_MATCH", "验证码错误");
            }
        }

        SysUser user = userService.getByUsername(loginAccount.getUsername());
        return getLoginResultByUser(loginAccount, user);
    }

    private LoginResult getLoginResultByUser(LoginAccount loginAccount, SysUser user) {
        if (user == null) {
            return new LoginResult("error", "account", null, null, "NOT_FOUND", "帐号或密码不正确");
        }
        if (user.getLoginFailUnlockTime() != null && user.getLoginFailUnlockTime().after(new Date())) {
            // 未到解锁时间
            return new LoginResult("error", "account", null, null, "ACCOUNT_LOCK", "此帐号当前已经锁定");
        }
        if (!passwordEncoder.matches(loginAccount.getPassword(), user.getPassword())) {
            userService.updateUserLoginFail(user);
            return new LoginResult("error", "account", null, null, "NOT_FOUND", "帐号或密码不正确");
        }
        if (user.getIsDisabled()) {
            return new LoginResult("error", "account", null, null, "ACCOUNT_DISABLED", "此帐号当前不可用");
        }
        return loginResult(user, loginAccount.getAutoLogin());
    }

    private LoginResult loginResult(SysUser user, boolean expirationLong) {
        // 生成的 token 有效期长时间为 30 天，短时间为 30 分钟
        return loginResult(user, expirationLong ? appProperties.getMemberMeTokenExpiration() : appProperties.getTokenExpiration());
    }

    private LoginResult loginResult(SysUser user, Duration expiration) {
        List<String> authorities = userService.getAllAuthorityByUserId(user.getId(), null);

        if (authorities.isEmpty()) {
            return new LoginResult("error", "account", null, null, "NO_AUTHORITIES", "此帐号没有定义权限");
        }

        JwtUserInfo jwtUserInfo = new JwtUserInfo();
        jwtUserInfo.setUserId(user.getId());
        jwtUserInfo.setType("ADMIN");
        jwtUserInfo.setUsername(user.getUsername());
        jwtUserInfo.setDisplayName(user.getName());
        jwtUserInfo.setExpiration(DateUtils.addSeconds(new Date(), (int) expiration.getSeconds()));

        LoginResult lr = new LoginResult();
        lr.setStatus("ok");
        lr.setToken("account");
        lr.setCurrentAuthority(ListUtils.join(",", authorities));
        lr.setToken(jwtUserInfo.toJwt());

        SysLoginLog loginLog = SysLoginLog.createWithUserId(user.getId());
        sysLoginLogService.create(loginLog);
        return lr;
    }

    @Override
    public LoginResult loginByCaptcha(LoginAccount loginAccount) {
        if(!verificationCodeService.verification(loginAccount.getMobile(), loginAccount.getCaptcha())) {
            return new LoginResult("error", "mobile", null, null, "INVALID_CODE", "验证码不正确");
        }
        SysUser user = userService.getByMobile(loginAccount.getMobile());
        return getLoginResultByUser(loginAccount, user);
    }

    @Override
    public LoginResult renewToken(JwtUserInfo jwtUserInfo) {
        if (jwtUserInfo != null) {
            SysUser user = userService.getByUsername(jwtUserInfo.getUsername());
            if(user != null) {
                if (user.getIsDisabled()) {
                    return new LoginResult("error", "account", null, null, "ACCOUNT_DISABLED", "此帐号当前不可用");
                }
                return loginResult(user, false);
            }
        }
        return null;
    }

    @Override
    public R createCaptcha(String phone) {
        return R.run(() -> verificationCodeService.sendCode(phone));
    }
}
